package com.gaogzhen.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gaogzhen.config.IdAutoConfiguration;
import com.gaogzhen.domain.Sms;
import com.gaogzhen.domain.User;
import com.gaogzhen.domain.UserAuthAuditRecord;
import com.gaogzhen.domain.UserAuthInfo;
import com.gaogzhen.dto.UserDto;
import com.gaogzhen.geetest.GeetestLib;
import com.gaogzhen.mapper.UserMapper;
import com.gaogzhen.mappers.UserDtoMapper;
import com.gaogzhen.model.*;
import com.gaogzhen.service.SmsService;
import com.gaogzhen.service.UserAuthAuditRecordService;
import com.gaogzhen.service.UserAuthInfoService;
import com.gaogzhen.service.UserService;
import com.gaogzhen.utils.CurrentUser;
import com.gaogzhen.utils.IdWorker;
import com.gaogzhen.vo.UserAuthInfoVo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @description ${description}
* @author Administrator
* @date 2022-06-07 11:46
* @version 1.0
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{

    @Value("${id.snowflake.appId:0}")
    private Long appId;

    @Value("${id.snowflake.machineId:0}")
    private Long machineId;

    @Autowired
    private UserAuthAuditRecordService userAuthAuditRecordService;

    @Autowired
    private UserAuthInfoService userAuthInfoService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private GeetestLib geetestLib;

    @Autowired
    private SmsService smsService;

    @Override
    public Page<User> findByPage(Page<User> page, String userId, String userName, String realName, String mobile, Integer status, Integer reviewsStatus) {
        page.addOrder(OrderItem.desc("last_update_time"));
        return page(page, Wrappers.lambdaQuery(User.class)
                            .eq(StringUtils.isNotBlank(userId), User::getId, userId)
                            .eq(ObjectUtils.isNotEmpty(status), User::getStatus, status)
                            .eq(ObjectUtils.isNotEmpty(reviewsStatus), User::getReviewsStatus, reviewsStatus)
                            .like(StringUtils.isNotBlank(userName), User::getUsername, userName)
                            .like(StringUtils.isNotBlank(realName), User::getRealName, realName)
                            .like(StringUtils.isNotBlank(mobile), User::getMobile, mobile)
        );
    }

    @Override
    public void updateUser(User user) {
        boolean update = updateById(user);
        if (!update) {
            throw new RuntimeException("更新会员失败！");
        }
    }

    @Override
    public void updateStatus(String id, Integer status) {
        boolean update = update(Wrappers.lambdaUpdate(User.class)
                .eq(User::getId, id)
                .set(User::getStatus, status)
        );
        if (!update) {
            throw new RuntimeException("更新会员状态失败！");
        }
    }

    @Override
    public Page<User> getDirectInvites(Page<User> page, Long userId) {
        page.addOrder(OrderItem.desc("last_update_time"));
        return page(page, Wrappers.lambdaQuery(User.class)
                .eq(ObjectUtils.allNotNull(userId), User::getId, userId)
        );
    }

    @Override
    public UserAuthInfoVo getAuthInfo(Long id) {
        // 查询用户
        User user = getById(id);
        // 查询审核记录
        List<UserAuthAuditRecord> userAuthAuditRecordList = null;
        List<UserAuthInfo> userAuthInfoList = null;
        if (ObjectUtils.isNotEmpty(user)) {
            if (ObjectUtils.isNotEmpty(user.getReviewsStatus())) {
                userAuthAuditRecordList = userAuthAuditRecordService.getList(id);
                // 查询会员认证详情列表-> 用户的身份信息
                // authCode:认证唯一标志
                if (CollectionUtils.isNotEmpty(userAuthAuditRecordList)) {
                    Long authCode = userAuthAuditRecordList.get(0).getAuthCode();
                    userAuthInfoList = userAuthInfoService.getList(null, authCode);
                }
            } else {
                userAuthAuditRecordList = Collections.emptyList();
            }
            if (CollectionUtils.isEmpty(userAuthInfoList)) {
                userAuthInfoList = userAuthInfoService.getList(id, null);
            }

        }
        return new UserAuthInfoVo(user, userAuthInfoList, userAuthAuditRecordList);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void updateUserAuthStatus(Long id, Integer authStatus, Long authCode, String remark) {
        User user = getById(id);
        if (ObjectUtils.isNotEmpty(user)) {
//            user.setAuthStatus(Integer.valueOf(authStatus)); 认证状态
            // 审核状态
            user.setReviewsStatus(authStatus);
            boolean update = updateById(user);
            if (!update) {
                throw new RuntimeException("更新用户审核状态失败!");
            }
        }
        UserAuthAuditRecord userAuthAuditRecord = new UserAuthAuditRecord();
        userAuthAuditRecord.setUserId(id);
        userAuthAuditRecord.setStatus(authStatus);
        userAuthAuditRecord.setAuditUserId(CurrentUser.getCurrentUserId());
        userAuthAuditRecord.setAuditUserName("-----");
        userAuthAuditRecord.setRemark(remark);
        boolean save = userAuthAuditRecordService.save(userAuthAuditRecord);
        if (!save) {
            throw new RuntimeException("审核失败！");
        }
    }

    @Override
    public boolean identifyVerify(Long currentUserId, UserAuthForm userAuthForm) {
        User user = getById(currentUserId);
        Assert.notNull(user, "认证用户不存在");
        Integer authStatus = user.getAuthStatus();
        if (!authStatus.equals(0)) {
            throw new IllegalArgumentException("该用户已经认证成功!");
        }
        // 执行认证
        authenticate(userAuthForm);
        // 实名认证
        boolean isOk = IdAutoConfiguration.identify(userAuthForm.getRealName(), userAuthForm.getIdCard());

        if (!isOk) {
            throw new IllegalArgumentException("该用户信息错误，请检查！");
        }
        // 认证成功，修改用户信息
        user.setAuthtime(new Date());
        user.setAuthStatus(1);
        user.setRealName(userAuthForm.getRealName());
        user.setIdCardType(userAuthForm.getIdCardType());
        user.setIdCard(userAuthForm.getIdCard());
        // 更新数据库用户信息
        return updateById(user);
    }

    /**
     * 极验认证
     * @param userAuthForm
     */
    private void authenticate(UserAuthForm userAuthForm) {
        userAuthForm.authenticate(geetestLib, redisTemplate);
    }

    @Override
    public User getById(Serializable id) {
        User user = super.getById(id);
        if (user == null) {
            throw new IllegalArgumentException("请输入正确的用户ID");
        }
        // 用户的高级认证状态
        Integer seniorAuthStatus = null;
        // 用户高级认证未通过原因
        String seniorAuthDesc = "";
        // 审核状态
        Integer reviewsStatus = user.getReviewsStatus();
        if (reviewsStatus == null) {
            seniorAuthStatus = 3;
            seniorAuthDesc = "资料未填写";
        } else {
            switch (reviewsStatus) {
                case 1:
                    seniorAuthStatus = 1;
                    seniorAuthDesc = "审核通过";
                    break;
                case 2:
                    // 审核未通过
                    seniorAuthStatus = 2;
                    // 查询拒绝原因-审核记录
                    List<UserAuthAuditRecord> records = userAuthAuditRecordService.getList((Long) id);
                    if (CollectionUtils.isNotEmpty(records)) {
                        UserAuthAuditRecord record = records.get(0);
                        seniorAuthDesc = record.getRemark();
                    } else {
                        seniorAuthDesc = "原因未知";
                    }
                    break;
                case 0:
                    seniorAuthStatus = 0;
                    seniorAuthDesc = "等待审核";
                    break;
                default:
                    break;
            }
        }
        user.setSeniorAuthStatus(seniorAuthStatus);
        user.setSeniorAuthDesc(seniorAuthDesc);
        return user;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void authUser(Long currentUserId, List<String> images) {
        // 图片校验
        if (CollectionUtils.isEmpty(images)) {
            throw new IllegalArgumentException("请上传认证图片");
        }
        // 获取当前用户信息
        User user = getById(currentUserId);
        if (ObjectUtils.isEmpty(user)) {
            throw new IllegalArgumentException("用户不存在");
        }
        // 更新用户认证信息
        // 雪花算法生成认证码
        IdWorker idWorker = new IdWorker(appId, machineId);
        long authCode = idWorker.nextId();

        int size = images.size();
        List<UserAuthInfo> userAuthInfoList = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            UserAuthInfo userAuthInfo = new UserAuthInfo();
            userAuthInfo.setUserId(currentUserId);
            userAuthInfo.setImageUrl(images.get(i));
            userAuthInfo.setSerialno(i + 1);
            userAuthInfo.setAuthCode(authCode);
            userAuthInfoList.add(userAuthInfo);
        }
        boolean success = userAuthInfoService.saveBatch(userAuthInfoList);
        if (!success) {
            throw new RuntimeException("更新认证信息失败");
        }
        // 更新用户信息
        user.setReviewsStatus(0);
        success = updateById(user);
        if (!success) {
            throw new RuntimeException("更新用户信息失败");
        }
    }

    @Override
    public void updatePhone(Long currentUserId, UpdatePhoneParam updatePhoneParam) {
        // 获取当前用户
        User user = getById(currentUserId);
        // 验证旧验证码
        String oldMobile = user.getMobile();
        checkValidateCode("SMS:CHANGE_PHONE_VERIFY:" + oldMobile, updatePhoneParam.getOldValidateCode(), "旧手机的验证码错误");
        // 验证新的验证码
        String newMobilePhone = updatePhoneParam.getNewMobilePhone();
        checkValidateCode("SMS:CHANGE_PHONE_VERIFY:" + newMobilePhone, updatePhoneParam.getValidateCode(), "新手机的验证码错误");
        // 更新用户手机号
        user.setMobile(newMobilePhone);
        boolean update = updateById(user);
        if (!update) {
            throw new RuntimeException("修改手机号失败");
        }
    }

    @Override
    public void checkTel(String mobile, String countryCode) {
        // 1 新的手机号没有被使用
        int count = count(new LambdaQueryWrapper<User>().eq(User::getMobile, mobile)
                .eq(User::getCountryCode, countryCode));
        if (count > 0) {
            throw new IllegalArgumentException("手机号已经被占用");
        }
        // 2 发送验证码
        Sms sms = new Sms();
        sms.setMobile(mobile);
        sms.setCountryCode(countryCode);
        sms.setTemplateCode("CHANGE_PHONE_VERIFY");
        smsService.sendSms(sms);
    }

    @Override
    public void updateLoginPassword(Long userId, UpdatePasswordParam updatePasswordParam) {
        updatePassword(userId, updatePasswordParam, "SMS:CHANGE_LOGIN_PWD_VERIFY:");
    }

    @Override
    public void updatePayPassword(Long userId, UpdatePasswordParam updatePasswordParam) {
        updatePassword(userId, updatePasswordParam, "SMS:CHANGE_PAY_PWD_VERIFY:");
    }



    private void updatePassword(Long userId, UpdatePasswordParam updatePasswordParam, String pwdType) {
        // 获取当前用户
        User user = getById(userId);
        if (user == null) {
            throw new IllegalArgumentException("非法访问");
        }
        // 校验手机验证码
        checkValidateCode(pwdType + user.getMobile(), updatePasswordParam.getValidateCode());
        // 校验旧密码
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        boolean matches = passwordEncoder.matches(updatePasswordParam.getOldPassword(), user.getPassword());
        if (!matches) {
            throw new IllegalArgumentException("旧密码不一致");
        }
        // 更新新密码
        user.setPassword(passwordEncoder.encode(updatePasswordParam.getNewPassword()));
        boolean update = updateById(user);
        if (!update) {
            throw new RuntimeException("更新密码失败");
        }
    }

    @Override
    public void resetPayPassword(Long userId, ResetPayPasswordParam resetPayPasswordParam) {
        // 获取当前用户
        User user = getById(userId);
        if (user == null) {
            throw new IllegalArgumentException("非法访问");
        }
        // 校验手机验证码
        checkValidateCode("SMS:FORGOT_PAY_PWD_VERIFY:" + user.getMobile(), resetPayPasswordParam.getValidateCode());
        // 更新交易密码
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        user.setPaypassword(passwordEncoder.encode(resetPayPasswordParam.getPayPassword()));
        boolean update = updateById(user);
        if (!update) {
            throw new RuntimeException("更新密码失败");
        }
    }

    @Override
    public List<User> getUserInvites(Long userId) {
        List<User> users = list(new LambdaQueryWrapper<User>().eq(User::getDirectInviteid, userId));
        if (CollectionUtils.isEmpty(users)) {
            return Collections.emptyList();
        }
        users.forEach(user -> {
            user.setPaypassword("******");
            user.setPassword("******");
            user.setAccessKeyId("******");
            user.setAccessKeySecret("******");
        });
        return users;
    }

    @Override
    public Map<Long, UserDto> getBriefUsers(List<Long> ids, String username, String mobile) {
        if (CollectionUtils.isEmpty(ids) && StringUtils.isEmpty(username) && StringUtils.isEmpty(mobile)) {
            return Collections.emptyMap();
        }

        List<User> users = list(new LambdaQueryWrapper<User>()
                .in(CollectionUtils.isNotEmpty(ids), User::getId, ids)
                .like(StringUtils.isNotEmpty(username), User::getUsername, username)
                .like(StringUtils.isNotEmpty(mobile), User::getMobile, mobile)
        );
        if (CollectionUtils.isEmpty(users)) {
            return Collections.emptyMap();
        }

        List<UserDto> userDtos = UserDtoMapper.INSTANCE.convert2Dto(users);
        return userDtos.stream().collect(Collectors.toMap(UserDto::getId, Function.identity()));
    }

    @Override
    public void register(RegisterParam registerParam) {
        // 校验手机号和邮箱不能同时为空
        String email = registerParam.getEmail();
        String mobile = registerParam.getMobile();
        if (StringUtils.isEmpty(mobile) && StringUtils.isEmpty(email)) {
            throw new IllegalArgumentException("手机号和邮箱不能同时为空");
        }
        // 极验校验
        registerParam.authenticate(geetestLib, redisTemplate);
        // 校验是否已经被注册
        int count = count(new LambdaQueryWrapper<User>()
                .eq(StringUtils.isNotEmpty(mobile), User::getMobile, mobile)
                .eq(StringUtils.isNotEmpty(email), User::getEmail, email));
        if (count > 0) {
            throw new RRException("该用户已经被注册");
        }
        // 构建新用户
        User u = structureUser(registerParam);

        // 更新数据库
        boolean save = save(u);
        if (!save) {
            throw new RuntimeException("用户注册失败");
        }
    }



    private User structureUser(RegisterParam registerParam) {
        User u = new User();
        u.setEmail(registerParam.getEmail());
        u.setMobile(registerParam.getMobile());
        String encode = new BCryptPasswordEncoder().encode(registerParam.getPassword());
        u.setPassword(encode);
        u.setPaypassSetting(false);
        u.setStatus(1);
        u.setType(1);
        u.setAuthStatus(0);
        u.setLogins(0);
        u.setInviteCode(RandomUtil.randomString(8));
        // 设置邀请人
        if (StringUtils.isNotEmpty(registerParam.getInvitationCode())) {
            User invite = getOne(new LambdaQueryWrapper<User>().eq(User::getInviteCode, registerParam.getInvitationCode()));
            if (invite != null) {
                u.setDirectInviteid(String.valueOf(invite.getId()));
                u.setInviteRelation(String.valueOf(invite.getId()));
            }
        }
        return u;
    }

    @Override
    public void resetPassword(ResetPasswordParam resetPasswordParam) {

        // 极验校验
        resetPasswordParam.authenticate(geetestLib, redisTemplate);
        // 校验手机验证码
        checkValidateCode("SMS:FORGOT_VERIFY:" + resetPasswordParam.getMobile(), resetPasswordParam.getValidateCode());
        // 校验手机号
        User user = getOne(new LambdaQueryWrapper<User>().eq(User::getMobile, resetPasswordParam.getMobile()));
        if (user == null) {
            throw new RRException("手机号为注册");
        }
        // 更新用户登录密码
        user.setPassword(new BCryptPasswordEncoder().encode(resetPasswordParam.getPassword()));
        boolean update = updateById(user);
        if (!update) {
            throw new RRException("更新密码失败");
        }
    }

    /**
     * 校验验证码
     * @param smsTemplate   短信模板
     * @param validateCode  验证码
     */
    private void checkValidateCode(String smsTemplate, String validateCode) {
        checkValidateCode(smsTemplate, validateCode, "验证码错误");
    }

    /**
     * 校验验证码
     * @param smsTemplate   短信模板
     * @param validateCode  验证码
     * @param errMsg        错误信息
     */
    private void checkValidateCode(String smsTemplate, String validateCode, String errMsg) {
        String code = String.valueOf(redisTemplate.opsForValue().get(smsTemplate));
        if (!StringUtils.equals(code, validateCode)) {
            throw new IllegalArgumentException(errMsg);
        }
    }

}
